package com.atxiaodei.nio.nioday01;

import org.junit.Test;

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

/**
 * @author wangmeng
 * @date 2020/9/13
 *
 * Stream API为我们提供了Stream.reduce用来实现集合元素的归约。reduce函数有三个参数：
 *
 * Identity标识：一个元素，它是归约操作的初始值，如果流为空，则为默认结果。
 * Accumulator累加器：具有两个参数的函数：归约运算的部分结果和流的下一个元素。
 * Combiner合并器（可选）：当归约并行化时，或当累加器参数的类型与累加器实现的类型不匹配时，用于合并归约操作的部分结果的函数。
 *
 * 我们先来理解累加器：
 * 阶段累加结果作为累加器的第一个参数
 * 集合遍历元素作为累加器的第二个参数
 * 不多说，直接干
 **/
public class StreamApi10集合元素归约 {

    /**
     * Integer类型归约
     *
     * reduce初始值为0，累加器可以是lambda表达式，也可以是方法引用。
     */
    @Test
    public void test1(){
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

        Integer sum = numbers.stream()
                .reduce(0, (x, y) -> x + y);
        System.out.println(sum);
    }

    /**
     * String类型规约
     * 不仅可以归约Integer类型，只要累加器参数类型能够匹配，可以对任何类型的集合进行归约计算。
     */
    @Test
    public void test2(){
        List<String> stringList = Arrays.asList("a","h","e","l","l","o");

        String str = stringList.stream()
                .reduce("", (x, y) -> x + y);
        System.out.println(str);
        // String 类里面有一个拼接字符串函数concat
        String str1 = stringList.stream()
                .reduce("",String::concat);
        System.out.println(str1);

    }
    StreamTestVo e1 = new StreamTestVo(1,23,"M","Rick","Beethovan");
    StreamTestVo e2 = new StreamTestVo(2,13,"F","Martina","Hengis");
    StreamTestVo e3 = new StreamTestVo(3,43,"M","Ricky","Martin");
    StreamTestVo e4 = new StreamTestVo(4,26,"M","Jon","Lowman");
    StreamTestVo e5 = new StreamTestVo(5,19,"F","Cristine","Maria");
    StreamTestVo e6 = new StreamTestVo(6,15,"M","David","Feezor");
    StreamTestVo e7 = new StreamTestVo(7,68,"F","Melissa","Roy");
    StreamTestVo e8 = new StreamTestVo(8,79,"M","Alex","Gussin");
    StreamTestVo e9 = new StreamTestVo(9,15,"F","Neetu","Singh");
    StreamTestVo e10 = new StreamTestVo(10,45,"M","Naveen","Jain");


    List<StreamTestVo> list = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);

    /**
     * 复杂对象的规约
     */
    @Test
    public void test3(){
        // 计算所有的员工的年龄总和。
        Integer reduce = list.stream()
                .map(StreamTestVo::getAge)
                .reduce(0, Integer::sum);
        System.out.println(reduce);
        Integer reduce2 = list.stream()
                .map(new Function<StreamTestVo, Integer>() {
                    @Override
                    public Integer apply(StreamTestVo streamTestVo) {
                        return streamTestVo.getAge();
                    }
                })
                .reduce(0, (x,y)->x+y);
        System.out.println(reduce2);
        /**
         * 总结：
         * 先用map将Stream流中的元素由Employee类型处理为Integer类型（age）。
         * 然后对Stream流中的Integer类型进行归约
         */
    }

    @Test
    public void test4(){
        /**
         * Combiner合并器的使用
         * 除了使用map函数实现类型转换后的集合归约，我们还可以用Combiner合并器来实现，这里第一次使用到了Combiner合并器。
         * 因为Stream流中的元素是Employee，累加器的返回值是Integer，所以二者的类型不匹配。
         * 这种情况下可以使用Combiner合并器对累加器的结果进行二次归约，相当于做了类型转换。
         *
         * 结论：计算结果和使用map进行数据类型转换的方式是一样的。
         */
        Integer sum = list.stream()
                .reduce(0, (x, y) -> x + y.getAge(), Integer::sum);//注意这里reduce方法有三个参数 没有用map过滤
        System.out.println(sum);

        /**
         * 并行流数据归约（使用合并器）
         * 对于大数据量的集合元素归约计算，更能体现出Stream并行流计算的威力。
         */
        Integer sum1 = list.stream()
                .parallel()
                .reduce(0, (x, y) -> x + y.getAge(), Integer::sum);
        System.out.println(sum1);

        Integer sum3 = list.stream()
                .parallel()
                .map(StreamTestVo::getAge)
                .reduce(0, Integer::sum, Integer::sum);
        System.out.println(sum3);

        Integer sum4 = list.parallelStream()
                .map(StreamTestVo::getAge)
                .reduce(0, Integer::sum, Integer::sum);
        System.out.println(sum4);
    }
}
